/**
 * 主从类型的通用操作函数
 */
import models from 'src-ssr/db/sequelize';
import moment from 'moment';
import * as tools from 'src-ssr/util/common';
import logs from 'src-ssr/util/logs';
import TableHelper from 'src-ssr/db/tableHelper';
import { Sequelize, Op } from 'sequelize';
import workflow from 'src-ssr/util/workflow';

/**
 * 取已删除模块
 * @param {*} modelName 模块名
 * @param {*} modelLabel 模块名称
 * @param {*} menuName 菜单名
 * @param {*} detailModelName 从表模块名称，即从表表，若空，默认为 ${modelName}_detail
 * @param {*} req 前端请求
 * @returns
 */
export const getRecycleData = async (modelName, modelLabel, menuName, detailModelName, req, isTenant = false) => {
  const { startRow, count, filter, sortBy, descending } = req.body;
  let ret = { rows: [], count: 0 };

  const where = { module: menuName };
  if (isTenant) {
    const user = await tools.getLoginUser(req.get('Authorization').split(' ')[1]);
    where.tenant_id = user.tid;
  }

  try {
    ret = await models.recycle.findAndCountAll({
      attributes: ['data', ['create_time', 'delete_time'], ['create_user_name', 'delete_user_name']],
      where,
      order: [['id', 'desc']],
      offset: startRow,
      limit: count,
      raw: true
    });
  } catch (e) {
    console.error(modelLabel + '回收列表出错', e);
  }

  const rows = ret.rows.map(row => {
    const { data, delete_time, delete_user_name } = row;
    return {
      ...data.master,
      [detailModelName]: data.detail,
      delete_time,
      delete_date: moment(delete_time, 'X').format('YYYY/MM/DD HH:mm'),
      delete_user_name
    }
  });

  return {
    data: rows,
    rowsNumber: ret.count
  };
};

/**
 * 新增数据
 * @param {string} modelName 模块名称，即主表名
 * @param {string} modelLabel 模块标签，即主表名称
 * @param {string} menuName 菜单名称
 * @param {string} detailModelName 从表模块名称，即从表表，若空，默认为 ${modelName}_detail
 * @param {string} associatedFieldName 从表与主表关系的字段名，若空，默认为 master_id
 * @param {string} keyFieldName 主表关联字段名，若空，默认为 id
 * @param {object} req 请求对象
 * @param {boolean} isTenant 是否租户数据
 * @param {boolean} detailRequired 是否强制需要明细表数据
 */
export const createData = async ({ modelName, modelLabel, menuName, detailModelName, associatedFieldName = 'master_id', keyFieldName = 'id' }, req, isTenant = false, detailRequired = true) => {
  const { formData } = req.body;

  const user = await tools.getLoginUser(req.get('Authorization').split(' ')[1]);
  const right = await tools.checkUserRight(user, menuName, ['func', 'add']);

  if (!right.and) {
    return { result: 'fail', message: `没有新增${modelLabel}权限` };
  }

  const data = TableHelper.filterField(modelName, formData);
  data.create_user_id = user.id;
  data.create_user_name = user.nickname;

  if (isTenant) data.tenant_id = user.tid; // 是否为租户数据

  if (typeof formData[detailModelName] === 'string') {
    formData[detailModelName] = JSON.parse(formData[detailModelName]);
  }
  if (detailRequired && formData[detailModelName].length === 0) {
    return { result: 'fail', message: '明细行数据不能为空' };
  }

  const detailData = TableHelper.filterField(detailModelName, formData[detailModelName] || []); // 明细表数据

  let newModel = {};
  try {
    newModel = await models[modelName].create(data);
    newModel = await models[modelName].findByPk(newModel.id);
    newModel = newModel.toJSON();

    if (detailData.length > 0) {
      detailData.forEach(row => {
        row[associatedFieldName] = newModel[keyFieldName];
        delete row.id;
        if (isTenant) row.tenant_id = user.tid;
      });
      await models[detailModelName].bulkCreate(detailData);
    }
  } catch (e) {
    console.error('新增' + modelLabel + '出错', e);
  }

  logs.recordLogs(user, { type: 'add', model: modelLabel, content: { formData } }, req);
  return { result: 'ok', id: newModel.id, uid: newModel.uid && tools.binToUuid(newModel.uid) };
};

/**
 * 新旧数据对比函数
 * @param {string} model 模型名称
 * @param {object} oldRows 旧数据
 * @param {object} newRows 新数据
 * @param {*} attributes 模型的属性定义
 * @param {*} user 当前操作的用户
 * @returns {array} 差异数据
 */
function contrastData (model, oldRows, newRows, attributes, user) {
  const changes = [];
  // 对比新旧数据
  for (const oldRow of oldRows) {
    const change = [];
    const newRow = newRows.find(v => v.id == oldRow.id);
    if (newRow) {
      for (const key in oldRow) {
        if (['create_time', 'create_user_id', 'create_user_name', 'modify_time', 'modify_user_id', 'modify_user_name', 'history', 'uid'].includes(key)) continue;

        let oldValue = oldRow[key];
        let newValue = newRow[key];

        if (oldValue === undefined || newValue === undefined) continue;

        // 数值型转成数字再对比
        if (['BIGINT', 'INTEGER', 'DECIMAL'].includes(attributes[key].type.key)) {
          oldValue = Number(oldValue);
          newValue = Number(newValue);
        } else if (['JSON'].includes(attributes[key].type.key)) { // JSON类型转成字符串再对比
          if (typeof oldValue === 'object') oldValue = JSON.stringify(oldValue);
          if (typeof newValue === 'object') newValue = JSON.stringify(newValue);
        }

        if (oldValue !== newValue) {
          change.push({
            field: key,
            oldValue: oldRow[key],
            newValue: newRow[key],
            comment: attributes[key].comment
          });
        }
      }
    }

    if (change.length > 0) {
      const item = {
        time: moment().format('X'),
        operate: 'update',
        user: user.nickname,
        model,
        change,
        id: oldRow.id
      };
      changes.push(item);
    }
  }

  return changes;
}
/**
 * 更新数据
 * @param {string} modelName 模块名称，即主表名
 * @param {string} modelLabel 模块标签，即主表名称
 * @param {string} menuName 菜单名称
 * @param {object} detailModelName 从表模块名称，即从表表，若空，默认为 ${modelName}_detail
 * @param {object} associatedFieldName 从表与主表关系的字段名，若空，默认为 master_id
 * @param {string} keyFieldName 主表关联字段名，若空，默认为 id
 * @param {object} req 请求对象
 * @param {boolean} isTenant 是否租户数据
 * @param {boolean} detailRequired 是否强制需要明细表数据
 */
export const updateData = async ({ modelName, modelLabel, menuName, detailModelName, associatedFieldName = 'master_id', keyFieldName = 'id' }, req, isTenant = false, detailRequired = true) => {
  const { id, formData } = req.body;

  const user = await tools.getLoginUser(req.get('Authorization').split(' ')[1]);
  const right = await tools.checkUserRight(user, menuName, ['func', 'edit']);

  const oriData = await models[modelName].findByPk(id);
  if (user.id != oriData.create_user_id && !right.edit) {
    return { result: 'fail', message: `没有修改${modelLabel}权限` };
  }

  const data = TableHelper.filterField(modelName, formData);
  data.modify_user_id = user.id;
  data.modify_user_name = user.nickname;

  if (typeof formData[detailModelName] === 'string') {
    formData[detailModelName] = JSON.parse(formData[detailModelName]);
  }
  if (detailRequired && formData[detailModelName].length === 0) {
    return { result: 'fail', message: '明细行数据不能为空' };
  }

  const detailData = TableHelper.filterField(detailModelName, formData[detailModelName] || []); // 明细表数据

  const detailHistory = [];
  try {
    // 主表数据
    const rawMasterData = await models[modelName].findByPk(id);

    if (isTenant && rawMasterData.tenant_id != user.tid) {
      return { result: 'fail', message: '用户与数据非同一组织不能修改' };
    }

    for (const key in data) {
      rawMasterData[key] = data[key];
    }
    await rawMasterData.save();

    // 从表数据
    const rawDetailData = await models[detailModelName].findAll({ where: { [associatedFieldName]: oriData[keyFieldName] }, raw: true });

    const detailDataIds = detailData.map(v => String(v.id));
    // 被删除的数据
    const removedData = rawDetailData.filter(v => !detailDataIds.includes(String(v.id)));

    if (removedData.length > 0) {
      detailHistory.push({
        operate: 'delete',
        model: detailModelName,
        data: removedData,
        time: moment().format('X'),
        user: user.nickname
      });
      await models[detailModelName].destroy({ where: { id: { [Op.in]: removedData.map(v => v.id) } } });
    }

    // 新增的数据
    const newData = detailData.filter(v => !v.id);
    if (newData.length > 0) {
      newData.forEach(row => {
        row[associatedFieldName] = oriData[keyFieldName];
        delete row.id;
        if (isTenant) row.tenant_id = user.tid;
      });
      const newRawData = await models[detailModelName].bulkCreate(newData);
      detailHistory.push({
        operate: 'insert',
        model: detailModelName,
        data: newRawData,
        time: moment().format('X'),
        user: user.nickname
      });
    }

    // 修改的数据
    const updateData = detailData.filter(v => v.id);
    const updateRawData = rawDetailData.filter(v => detailDataIds.includes(String(v.id)));
    detailHistory.push(...contrastData(detailModelName, updateRawData, updateData, models[detailModelName].rawAttributes, user));
    if (updateData.length > 0) {
      for (const row of updateData) {
        const id = row.id;
        delete row.id;
        await models[detailModelName].update(row, { where: { id } });
      }
    }

    // 明细数据变动历史
    if (detailHistory.length > 0) {
      const history = rawMasterData.toJSON().history || [];
      history.push(...detailHistory);
      rawMasterData.history = history;
      await rawMasterData.save();
    }
  } catch (e) {
    console.error('修改' + modelLabel + '出错', e);
  }

  logs.recordLogs(user, { type: 'edit', model: modelLabel, content: { formData } }, req);
  return { result: 'ok', id };
};

/**
 * 删除数据
 * @param {string} modelName 模块名称，即主表名
 * @param {string} modelLabel 模块标签，即主表名称
 * @param {string} menuName 菜单名称
 * @param {object} detailModelName 从表模块名称，即从表表，若空，默认为 ${modelName}_detail
 * @param {object} associatedFieldName 从表与主表关系的字段名，若空，默认为 master_id
 * @param {string} keyFieldName 主表关联字段名，若空，默认为 id
 * @param {object} snapshot 数据快照
 * @param {object} req 请求对象
 */
export const destroyData = async ({ modelName, modelLabel, menuName, detailModelName, associatedFieldName = 'master_id', keyFieldName = 'id', snapshot }, req, isTenant = false) => {
  const { id } = req.body;

  const user = await tools.getLoginUser(req.get('Authorization').split(' ')[1]);
  const right = await tools.checkUserRight(user, menuName, ['func', 'edit']);

  let masterData = null, detailData = null;
  if (snapshot) { // 若有快照，则用快照
    detailData = snapshot[detailModelName];
    delete snapshot[detailModelName];
    masterData = snapshot;
  } else { // 若无快照，则取数据
    masterData = (await models[modelName].findByPk(id)).toJSON();
    detailData = (await models[detailModelName].findAll({ where: { [associatedFieldName]: masterData[keyFieldName] } })).toJSON();
  }
  if (user.id != masterData.create_user_id && !right.edit) {
    return { result: 'fail', message: `没有删除${modelLabel}权限` };
  }
  if (isTenant && masterData.tenant_id != user.tid) {
    return { result: 'fail', message: '用户与数据非同一组织不能删除' };
  }
  logs.recordLogs(user, { type: 'delete', model: modelLabel, content: { master: masterData, detail: detailData } }, req);

  try {
    await models[modelName].destroy({ where: { id } });
    await models[detailModelName].destroy({ where: { [associatedFieldName]: masterData[keyFieldName] } });
    // 放入回收站
    // await models.recycle.create({
    //   module: menuName,
    //   data: { master: masterData, detail: detailData },
    //   create_time: moment().format('X'),
    //   create_user_id: user.id,
    //   create_user_name: user.nickname
    // });
  } catch (e) {
    console.error('删除' + modelLabel + '出错', e);
  }

  return { result: 'ok' };
};

/**
 * 审批数据
 * @param {string} modelName 模块名称，即主表名
 * @param {string} modelLabel 模块标签，即主表名称
 * @param {string} menuName 菜单名称
 * @param {object} snapshot 数据快照
 * @param {object} req 请求对象
 */
export const approveData = async ({ modelName, modelLabel, menuName, snapshot }, req) => {
  const { id, state, opinion } = req.body;

  // 检查该人是否有审批权限
  const user = await tools.getLoginUser(req.get('Authorization').split(' ')[1]);
  const right = await tools.checkUserRight(user, menuName, ['approve']);
  if (!right.approve) { // 是否有查看全部的权限
    return { result: 'fail', message: `无${modelLabel}的审批权限` };
  }

  if (!['pass', 'reject', 'none'].includes(state)) {
    return { result: 'fail', message: `无此${state}审批操作类型` };
  }

  const obj = await models[modelName].findByPk(id);
  if (!obj) {
    return { result: 'fail', message: '无此审批对象' };
  }

  switch (state) {
    case 'pass':
    case 'reject':
      obj.approval_time = moment().format('X')
      obj.approval_state = state;
      obj.approval_opinion = opinion;
      break;
    case 'none':
      obj.approval_time = null;
      obj.approval_state = state;
      obj.approval_opinion = '';
      break;
  }
  await obj.save(); // 保存审批结果

  // 审批通过，记录数据快照
  if (state === 'pass') {
    // 把JSON全展开后，再stringify
    tools.parseAllJSON(snapshot);
    await models.approval_snapshot.create({
      module: menuName,
      module_label: modelLabel,
      key_uid: obj.uid,
      state,
      snapshot,
      approval_user_id: user.id,
      approval_user_name: user.nickname,
      approval_time: obj.approval_time
    });
  }

  // 记录操作日志
  logs.recordLogs(user, { type: 'approve', model: modelLabel, content: { snapshot } }, req);

  // 通知创建人审批结果
  workflow.sendCommand(obj.create_user_id, {
    message: `您的${modelLabel} ${obj.bill_no} ${global.dictObj.approvalState[state].label}`,
    module: `/main/${menuName}/detail/${tools.encrypt(id)}`
  }, '通知', user.id, {
    id: 'notice',
    data: {
      keyword1: `${modelLabel} ${obj.bill_no} ${global.dictObj.approvalState[state].label}`,
      keyword2: global.dictObj.approvalState[state].label,
      keyword3: `审批人${user.nickname}`
    }
  });
};

/**
 * 取最新的快照数据
 * @param {*} module 模块名称
 * @param {*} uid 数据id
 * @returns {*} 快照数据
 */
export const getSnapshot = async (module, uid) => {
  // 取最新一条快照的id
  const mid = await models.approval_snapshot.max('id', {
    where: {
      module,
      key_uid: uid,
      state: 'pass'
    }
  });

  // 取快照数据
  let snapshot = null;
  if (mid) {
    snapshot = await models.approval_snapshot.findOne({
      attributes: ['snapshot'],
      where: {
        id: mid
      }
    });
    snapshot = snapshot.snapshot
  }

  return snapshot;
};

/**
 * 更新多行数据
 * @param {string} modelName 模块名称，即主表名
 * @param {string} modelLabel 模块标签，即主表名称
 * @param {object} detailModelName 从表模块名称，即从表表，若空，默认为 ${modelName}_detail
 * @param {object} keyFieldName 从表与主表关系的字段名，若空，默认为 master_id
 * @param {object} req 请求对象
 */
export const updateDetailData = async ({ modelName, modelLabel, keyFieldName }, req, isTenant = false) => {
  const { id, formData } = req.body;

  const user = await tools.getLoginUser(req.get('Authorization').split(' ')[1]);

  if (typeof formData[modelName] === 'string') {
    formData[modelName] = JSON.parse(formData[modelName]);
  }

  const detailData = TableHelper.filterField(modelName, formData[modelName] || []); // 明细表数据

  try {
    // 从表数据
    const rawDetailData = await models[modelName].findAll({ where: { [keyFieldName]: id }, raw: true });

    const detailDataIds = detailData.map(v => String(v.id));

    // 被删除的数据
    const removedData = rawDetailData.filter(v => !detailDataIds.includes(String(v.id)));
    if (removedData.length > 0) {
      await models[modelName].destroy({ where: { id: { [Op.in]: removedData.map(v => v.id) } } });
    }

    // 新增的数据
    const newData = detailData.filter(v => !v.id);
    if (newData.length > 0) {
      newData.forEach(row => {
        row[keyFieldName] = id;
        delete row.id;
        if (isTenant) row.tenant_id = user.tid;
      });
      await models[modelName].bulkCreate(newData);
    }

    // 修改的数据
    const updateData = detailData.filter(v => v.id);
    if (updateData.length > 0) {
      for (const row of updateData) {
        const id = row.id;
        delete row.id;
        await models[modelName].update(row, { where: { id } });
      }
    }
  } catch (e) {
    console.error('修改' + modelLabel + '出错', e);
  }

  logs.recordLogs(user, { type: 'edit', model: modelLabel, content: { formData } }, req);
  return { result: 'ok', id };
};
